﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;
using EnvDTE;
using SmartStringResources.Dte;
using SmartStringResources.Properties.Strings;
using CodeNamespace = System.CodeDom.CodeNamespace;

namespace SmartStringResources
{
	internal class CompileUnitGenerator
	{
	    private readonly CodeDomProvider _provider;

	    public CompileUnitGenerator(CodeDomProvider provider)
	    {
	        _provider = provider;
	    }

	    internal CodeCompileUnit GenerateCompileUnit(TextReader resource, ResourceFileInfo resourceFileInfo, GeneratorOptions options = null)
		{
			if (options == null)
				options = new GeneratorOptions();

			if (options.MaxAreaDepth < 0)
				throw new ArgumentOutOfRangeException(Resources.MaxAreaDepthMustBeGreaterOrEqualToZero());

			var targetNamespace = options.TargetResourcesNamespace ?? (string.IsNullOrEmpty(resourceFileInfo.ResourcesNamespace) ? Constants.DefaultNamespaceSufix : string.Concat(resourceFileInfo.ResourcesNamespace, ".", Constants.DefaultNamespaceSufix));
			var targetClassName = options.TargetResourcesClassName ?? resourceFileInfo.ResourcesClassName;
			var targetKeysNamespace = options.TargetResourceKeysNamespace ?? targetNamespace;
			var targetKeysClassName = options.TargetResourceKeysClassName ?? string.Concat(targetClassName, Constants.DefaultKeysClassSufix);

			var variables = new TemplateVariables { DefaultResourcesNamespaceTemplate = resourceFileInfo.ResourcesNamespace, DefaultResourcesClassNameTemplate = resourceFileInfo.ResourcesClassName, TargetResourcesNamespaceTemplate = () => targetNamespace, TargetResourcesClassNameTemplate = () => targetClassName };

			targetNamespace = EvaluateVariables(targetNamespace, variables);
			targetClassName = EvaluateVariables(targetClassName, variables);
			targetKeysNamespace = EvaluateVariables(targetKeysNamespace, variables);
			targetKeysClassName = EvaluateVariables(targetKeysClassName, variables);

			if (targetNamespace == targetKeysNamespace && targetClassName == targetKeysClassName)
				throw new ArgumentException(Resources.ResourceNamespaceAndClassNameShouldNotEqualKeysNamespaceAndClassName());

			var compileUnit = new CodeCompileUnit();
			var usingStatements = new CodeNamespace();
			usingStatements.Imports.Add(new CodeNamespaceImport(typeof(StringFormatWithExtension).Namespace));
			compileUnit.Namespaces.Add(usingStatements);

			var resourceFile = new ResourceFile(targetClassName, _provider, options);
			var rootArea = resourceFile.Build(resource);

			if (options.GenerateResources)
			{
			    var smartResourcesBuilder = new SmartResourcesBuilder(rootArea, resourceFileInfo.ResourceClassFullName);
				var codeNamespace = new CodeNamespace(targetNamespace);
				compileUnit.Namespaces.Add(codeNamespace);
				codeNamespace.Types.Add(smartResourcesBuilder.Build(options.ResourcesAccessibilityLevel));
			}

			if (options.GenerateResourceKeys)
			{
			    var resourceKeysBuilder = new ResourceKeysBuilder(rootArea);
				var keysNamespace = new CodeNamespace(targetKeysNamespace);
				compileUnit.Namespaces.Add(keysNamespace);
				keysNamespace.Types.Add(resourceKeysBuilder.Build(targetKeysClassName, options.ResourceKeysAccessibilityLevel));
			}

			return compileUnit;
		}

		private string EvaluateVariables(string template, TemplateVariables variables)
		{
			return template
				.Replace(GeneratorOptions.DefaultResourcesNamespaceTemplate, variables.DefaultResourcesNamespaceTemplate)
				.Replace(GeneratorOptions.DefaultResourcesClassNameTemplate, variables.DefaultResourcesClassNameTemplate)
				.Replace(GeneratorOptions.TargetResourcesNamespaceTemplate, variables.TargetResourcesNamespaceTemplate())
				.Replace(GeneratorOptions.TargetResourcesClassNameTemplate, variables.TargetResourcesClassNameTemplate());
		}
	}
}
